//------------------------------------------------------------------------------
//
//   author: 小兵（aosnow@yeah.net）
//   create: 2012-5-25 下午3:36:33
//    class: Service - 服务端服务请求
//
//------------------------------------------------------------------------------

package starfire.rpc.http
{
	import starfire.events.ServiceEvent;
	import starfire.namespaces.sn_internal;

	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoaderDataFormat;
	import flash.utils.ByteArray;

	use namespace sn_internal;

	public class HttpService extends EventDispatcher
	{

		//--------------------------------------------------------------------------
		//
		//   Class variable
		//
		//--------------------------------------------------------------------------

		private var _httpLoader:HttpLoader;
		private var _name:String;

		private var _connect:Boolean;
		private var _requestComplete:Boolean;

		public var complete:Vector.<Function>;
		public var error:Vector.<Function>;
		public var progress:Vector.<Function>;
		public var timeOut:Vector.<Function>;

		//--------------------------------------------------------------------------
		//
		//   Class constructor
		//
		//--------------------------------------------------------------------------

		public function HttpService( name:String, msg:HttpRequestMessage )
		{
			_name = name;
			_httpLoader = createHttpLoader( msg );
		}

		public function createHttpLoader( msg:HttpRequestMessage ):HttpLoader
		{
			var loader:HttpLoader = new HttpLoader( msg );
			loader.contentType = msg.contentType != null ? msg.contentType : HttpRequestMessage.CONTENT_TYPE_FORM;
			loader.resultFormat = msg.resultFormat != null ? msg.resultFormat : HttpResult.RESULT_FORMAT_OBJECT;

			// HTTP 通信一般使用文本字符串形式，无论是 XML 格式内容或者其它字符串
			loader.dataFormat = URLLoaderDataFormat.TEXT;

			complete = new Vector.<Function>;
			error = new Vector.<Function>;
			progress = new Vector.<Function>;
			timeOut = new Vector.<Function>;

			return loader;
		}

		//--------------------------------------------------------------------------
		//
		//   Class properties
		//
		//--------------------------------------------------------------------------

		/** 服务名称（请求命令类型）  **/
		public function get name():String
		{
			return _name;
		}

		/** 服务请求取到的数据 **/
		public function get data():Object
		{
			return _httpLoader.result;
		}

		/**
		 * 设置请求服务时需要发送的数据
		 * @param value						- 发送数据
		 */
		public function set dataRequest( value:Object ):void
		{
			_httpLoader.dataRequest = value;
		}

		public function get dataRequest():Object
		{
			return _httpLoader.dataRequest;
		}

		public function get resultFormat():String
		{
			return _httpLoader.resultFormat;
		}

		/**
		 * <p>指示如何反序列化由 HTTP 调用返回的结果的值。该项目的值根据以下条件确定：</p>
		 * <ul>
		 * <li>返回的是 XML 还是名称/值对。</li>
		 * <li>访问结果的方式；可以将结果作为 object、text 或 XML 进行访问。</li>
		 * </ul>
		 * <p>默认值为 object。允许使用的值包括：</p>
		 *
		 * <ul>
		 * <li>object 返回的值为 XML 并且按照 ActionScript 对象树分析。此为默认。</li>
		 * <li>array 返回的值是 XML 并且按照 ActionScript 对象树分析。但是，如果顶级对象不是数组，
		 * 将创建一个新数组并且将结果设置为第一个项目。如果 makeObjectsBindable 为 true，则该数组
		 * 将被包装在 ArrayCollection 中。</li>
		 * <li>xml 返回的值为 XML 并且作为 ActionScript XMLnode 对象中的文本 XML 返回。</li>
		 * <li>flashvars 返回的值是包含由 &amp; 符号分隔的名称=值对的文本，该文本被分析为 ActionScript 对象。</li>
		 * <li>text 返回的值为文本并且未经处理。</li>
		 * </ul>
		 */
		public function set resultFormat( value:String ):void
		{
			_httpLoader.resultFormat = value;
		}

		/** 请求是否成功完成，完成后才能取得数据  **/
		public function get requestComplete():Boolean
		{
			return _requestComplete;
		}

		/** 请求是否成功连接到服务端  **/
		public function get connect():Boolean
		{
			return _connect;
		}

		//--------------------------------------------------------------------------
		//
		//   Class methods
		//
		//--------------------------------------------------------------------------

		/**
		 * 发送请求（若在发送前已单独设置回调，则此处设置回调将被忽略）
		 * @param dataObject				- 发送请求时携带的数据
		 * @param complete					- 请求完成时的回调
		 * @param error						- 请求操作发生错误时的回调
		 * @param progress					- 请求进度进行时的回调
		 * @param timeOut					- 请求超时终止时的回调
		 *
		 * <p>
		 * <b>complete</b> 示例：<br><code>function complete( data:Object ):void{}
		 * </code></p>
		 * <p>
		 * <b>error</b> 示例：<br>
		 * event 参数的类型可能是以下3种事件类型中的任何一种：<br>
		 * HTTPStatusEvent、SecurityErrorEvent、IOErrorEvent<br><br>
		 * <pre>
*function error( event:Event ):void{
*	if(event is HTTPStatusEvent)
*	{
*		trace("HTTPStatusEvent 状态", ( event as HTTPStatusEvent ).status);
*	}
*	else if(event is SecurityErrorEvent)
*	{
*		trace("服务请求发生安全错误");
*	}
*	else if(event is IOErrorEvent)
*	{
*		trace("服务请求发生 IO 错误");
*	}
*}
* </pre></p>
* <p><b>progress</b> 示例：<br><code>
* info为加载进度数据数组：[ bytesLoaded:Number, bytesTotal:Number ]<br>
* function progress( info:Array ):void{}
* </code></p>
* <p><b>timeOut</b> 示例：<br><code>function timeOut():void{}
* </code></p>
*/
		public function send( dataObject:Object = null, // 发送数据
							  complete:Function = null, // 完成回调
							  error:Function = null, // 错误回调
							  progress:Function = null, // 进度回调
							  timeOut:Function = null // 超时回调
							  ):void
		{
			configHandlers();

			_connect = false;
			_requestComplete = false;

			setCallback( complete, "complete" );
			setCallback( error, "error" );
			setCallback( progress, "progress" );
			setCallback( timeOut, "timeOut" );

			var data:Object = dataRequest ? dataRequest : dataObject;

			if( data is ByteArray )
			{
				_httpLoader.dataFormat = URLLoaderDataFormat.BINARY;
				_httpLoader.resultFormat = HttpResult.RESULT_FORMAT_BINARY;
			}

			_httpLoader.send();
		}

		public function setCallback( func:Function, type:String = "complete" ):void
		{
			if( func == null )
				return;

			var funcs:Vector.<Function> = this[ type ];

			for each( var f:Function in funcs )
			{
				if( f == func )
					return;
			}

			funcs[ funcs.length ] = func;
		}

		/** 重置请求操作，释放资源，待重新发送  **/
		public function release():void
		{
			try
			{
				_httpLoader.close();
			}
			catch( error:Error )
			{
			}
		}

		public function destroy():void
		{
			removeHandlers();

			release();
			_httpLoader = null;

			if( complete.length )
				complete.splice( 0, complete.length );

			if( error.length )
				error.splice( 0, error.length );

			if( progress.length )
				progress.splice( 0, progress.length );

			if( timeOut.length )
				timeOut.splice( 0, timeOut.length );

			complete = null;
			error = null;
			progress = null;
			timeOut = null;
		}

		//--------------------------------------------------------------------------
		//
		//   服务请求事件处理
		//
		//--------------------------------------------------------------------------

		private function configHandlers():void
		{
			_httpLoader.addEventListener( Event.COMPLETE, completeHandler );
			_httpLoader.addEventListener( Event.OPEN, openHandler );
			_httpLoader.addEventListener( ProgressEvent.PROGRESS, progressHandler );
			_httpLoader.addEventListener( SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler );
			_httpLoader.addEventListener( HTTPStatusEvent.HTTP_STATUS, httpStatusHandler );
			_httpLoader.addEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_httpLoader.addEventListener( HttpLoader.REQUEST_TIMEOUT, timeOutHandler );
		}

		private function removeHandlers():void
		{
			_httpLoader.removeEventListener( Event.COMPLETE, completeHandler );
			_httpLoader.removeEventListener( Event.OPEN, openHandler );
			_httpLoader.removeEventListener( ProgressEvent.PROGRESS, progressHandler );
			_httpLoader.removeEventListener( SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler );
			_httpLoader.removeEventListener( HTTPStatusEvent.HTTP_STATUS, httpStatusHandler );
			_httpLoader.removeEventListener( IOErrorEvent.IO_ERROR, ioErrorHandler );
			_httpLoader.removeEventListener( HttpLoader.REQUEST_TIMEOUT, timeOutHandler );
		}

		private function execCallback( callback:String, data:* = null ):void
		{
			var funcs:Vector.<Function> = this[ callback ];

			if( funcs.length )
			{
				for each( var f:Function in funcs )
				{
					if( data )
						f( data );
					else
						f();
				}
			}
		}

		private function completeHandler( event:Event ):void
		{
			removeHandlers();
			_requestComplete = true;

			// 成功完成取消超时检测
			_httpLoader.releaseTimer();

			// 将 Http 信息配置对象置为就绪状态 
			_httpLoader._message.setInactive( true );

			var dataEvent:ServiceEvent = new ServiceEvent( ServiceEvent.COMPLETE, _name, _httpLoader.result );
			dispatchEvent( dataEvent );

			if( complete.length )
			{
				execCallback( "complete", dataEvent );
			}
		}

		private function openHandler( event:Event ):void
		{
		}

		// 进度信息
		private function progressHandler( event:ProgressEvent ):void
		{
			if( progress.length )
			{
				execCallback( "progress", [ event.bytesLoaded, event.bytesTotal ]);
			}
		}

		private function securityErrorHandler( event:SecurityErrorEvent ):void
		{
			if( error.length )
			{
				execCallback( "error", event );
			}
		}

		private function httpStatusHandler( event:HTTPStatusEvent ):void
		{
			switch( event.status )
			{
				case 200:
					_connect = true;
					break;
				case 400:
				case 401:
				case 402:
				case 403:
				case 404: // 网页地址错误
				case 405:
				case 406:
				case 407:
				case 408:
				case 409:
				case 410:
				case 411:
				case 412:
				case 413:
				case 414:
				case 415:
				case 449:
				case 500: // 网页脚本错误
				case 501:
				case 502:
				case 503:
				case 504:
				case 505:
					if( error.length )
					{
						execCallback( "error", event );
					}
					break;
			}
		}

		private function ioErrorHandler( event:IOErrorEvent ):void
		{
			if( error.length )
			{
				execCallback( "error", event );
			}
		}

		private function timeOutHandler( event:ServiceEvent ):void
		{
			if( timeOut.length )
			{
				execCallback( "timeOut", _httpLoader._message );
			}
		}

		//--------------------------------------------------------------------------
		//
		//  Notification Pool
		//
		//--------------------------------------------------------------------------

		private static var _httpServicePool:Vector.<HttpService> = new Vector.<HttpService>;

		sn_internal static function fromPool( name:String, msg:HttpRequestMessage ):HttpService
		{
			if( _httpServicePool.length )
				return _httpServicePool.pop().reset( name, msg );
			else
				return new HttpService( name, msg );
		}

		sn_internal static function toPool( httpService:HttpService ):void
		{
			_httpServicePool[ _httpServicePool.length ] = httpService;
		}

		sn_internal function reset( name:String, msg:HttpRequestMessage ):HttpService
		{
			destroy();

			_name = name;
			_httpLoader = createHttpLoader( msg );
			_connect = false;
			_requestComplete = false;

			return this;
		}
	}
}
